import { AbstractList, IListConstructor } from "./list-type";
import { ListNode } from "./list-node";
import { objectToString, objectToStringFormat, arrayList } from "../../shared";
/**
 * 动态数组构造器
 */
export interface ArrayIListConstructor<T> extends IListConstructor<T> {
  new (datas: T[]): ArrayList<T>;
  readonly [Symbol.species]: ArrayIListConstructor<T>;
  readonly prototype: ArrayList<T>;
  isArrayList<T>(obj: unknown): obj is ArrayList<T>;
  of<T>(...datas: T[]): ArrayList<T>;
  from<T>(datas: T[]): ArrayList<T>;
}
/**
 * 判断是否是动态数组
 * @param obj 任意对象
 */
export function isArrayList<T>(obj: unknown): obj is ArrayList<T> {
  return obj instanceof ArrayList;
}
/**
 * 动态数组，
 * 定长，
 * 数组元素类型唯一
 */
export class ArrayList<T> extends AbstractList<T> {
  static [Symbol.hasInstance](instance: unknown): boolean {
    return objectToString(instance) === objectToStringFormat(arrayList);
  }
  static from<T>(datas: T[]): ArrayList<T> {
    return new ArrayList(datas);
  }
  static of<T>(...datas: T[]): ArrayList<T> {
    return new ArrayList(datas);
  }
  static isArrayList<T>(obj: unknown): obj is ArrayList<T> {
    return isArrayList(obj);
  }
  static get [Symbol.species](): ArrayIListConstructor<any> {
    return ArrayList;
  }
  [Symbol.toStringTag] = arrayList;
  get first(): T | null {
    if (this.empty) return null;
    return this._elements[0];
  }
  get last(): T | null {
    if (this.empty) return null;
    return this._elements[this.size - 1];
  }
  private _elements: T[] = [];
  constructor(datas: T[] = []) {
    super();
    this._elements = datas;
    this._size = datas.length;
  }
  add(element: T, index: number = this._size): void {
    this.checkAddRange(index);
    if (index === this._size) this._elements[index] = element;
    else this._elements.splice(index, 0, element);
    this._size++;
  }
  delete(index: number): T {
    this.checkRange(index);
    this._size--;
    return this._elements.splice(index, 1)[0];
  }
  get(index: number): T {
    this.checkRange(index);
    return this._elements[index];
  }
  set(index: number, element: T): T {
    this.checkRange(index);
    const oldEl = this.get(index);
    this._elements[index] = element;
    return oldEl;
  }
  entries(): IterableIterator<[number, T]> {
    return this._elements.entries();
  }
  clear(): void {
    this._elements.length = 0;
    super.clear();
  }
  /* istanbul ignore next */
  protected entriesForNode(): IterableIterator<[number, ListNode<T>]> {
    return {} as IterableIterator<[number, ListNode<T>]>;
  }
}
